// <com.woyouqiu.Copyright>
// --------------------------------------------------------------
// <copyright>上海有求网络科技有限公司 2015</copyright>
// <Solution>Vecen.BangChuang</Solution>
// <Project>Vecen.BangChuang.Core</Project>
// <FileName>SmsDomainService.cs</FileName>
// <CreateTime>2017-04-29 20:17</CreateTime>
// <Author>何苗</Author>
// <Email>hemiao@woyouqiu.com</Email>
// <log date="2017-04-29 20:17" version="00001">创建</log>
// --------------------------------------------------------------
// </com.woyouqiu.Copyright>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Abp.Dependency;
using Abp.Configuration;
using Abp.Domain.Repositories;
using Abp.Threading;
using Abp.UI;
using Vecen.BangChuang.BangChuang.Common;
using Vecen.BangChuang.Net.Sms;
using Vecen.BangChuang.Validation;

namespace Vecen.BangChuang.BangChuang.DomainServices.Common {
    public class ValidationCodeDomainService: BangChuangDomainServiceBase, ITransientDependency
    {
        private readonly IRepository<ValidationCode> _validationCodeRepository;
        private readonly ISmsSender _smsSender;

        public ValidationCodeDomainService(IRepository<ValidationCode> validationCodeRepository, ISmsSender smsSender) {
            _validationCodeRepository = validationCodeRepository;
            _smsSender = smsSender;
        }

        /// <summary>
        /// 发送验证码
        /// </summary>
        /// <param name="phoneNumber"></param>
        /// <param name="validationCode"></param>
        /// <param name="codeType"></param>
        /// <returns></returns>
        public async Task SendValidationCode(string phoneNumber, string validationCode, ValidationCodeType codeType) {
            if(!ValidationHelper.IsMobilePhone(phoneNumber)) {
                throw new UserFriendlyException("请求失败!", "手机号格式不正确");
            }
            //同手机号两次短信最小间隔时间
            int validationCodeIntervalTime =
                await SettingManager.GetSettingValueForApplicationAsync<int>(BangChuangConsts.SettingsName
                    .Validation_Code_IntervalTime);

            //同手机号单位时间内最大发送短信数量
            var limitCount =
                await SettingManager.GetSettingValueForApplicationAsync<int>(BangChuangConsts.SettingsName
                    .Validation_Code_LimitCount);

            var lastSendRecord = await _validationCodeRepository.FirstOrDefaultAsync(c => c.Target == phoneNumber);
            
            if(lastSendRecord!= null && lastSendRecord.CreationTime.AddMinutes(validationCodeIntervalTime) > DateTime.Now) {
                throw new UserFriendlyException("请求失败!", "刚刚发过短信,请过会再发吧!");
            }
            var sendCount =
                await _validationCodeRepository.CountAsync(
                    c => c.CreationTime > DateTime.Now.AddHours(-1) && c.Target == phoneNumber);

            if(sendCount >= limitCount) {
                throw new UserFriendlyException("请求失败!", "单位时间内发送短信次数过多!请稍候再试!");
            }

            await _smsSender.SendAsync(phoneNumber, GetTemplateCode(codeType), new List<string>() {validationCode});
            var sms = new ValidationCode();
            sms.Target = phoneNumber;
            sms.Code = validationCode;
            sms.ValidateType = codeType.ToString();
            sms.TargetType = ValidationCodeTargetType.PhoneNumber.ToString();
            sms.IsValidated = false;
            sms.IsObsolete = false;
            sms.Id = await _validationCodeRepository.InsertAndGetIdAsync(sms);

            var obsoletes = _validationCodeRepository.GetAll()
                .Where(c => c.Target == phoneNumber)
                .Where(c => c.Id != sms.Id)
                .Where(c => c.IsObsolete == false)
                .ToList();
            foreach(var obsolete in obsoletes) {
                obsolete.IsObsolete = true;
                await _validationCodeRepository.UpdateAsync(obsolete);
            }
        }

        /// <summary>
        /// 获得短信模板编号
        /// </summary>
        /// <param name="codeType"></param>
        /// <returns></returns>
        private string GetTemplateCode(ValidationCodeType codeType) {
            switch(codeType) {
                case ValidationCodeType.Register:
                    return SettingManager.GetSettingValueForApplication(BangChuangConsts.SettingsName.ThirdApi
                        .Validation_Code_Sms_Register_TemplateCode);

                case ValidationCodeType.ChangePhoneNumber:
                    return SettingManager.GetSettingValueForApplication(BangChuangConsts.SettingsName.ThirdApi
                        .Validation_Code_Sms_ChangePhoneNumber_TemplateCode);

                case ValidationCodeType.QuickLogin:
                    return SettingManager.GetSettingValueForApplication(BangChuangConsts.SettingsName.ThirdApi
                        .Validation_Code_Sms_QuickLogin_TemplateCode);

                case ValidationCodeType.ChangePassword:
                    return SettingManager.GetSettingValueForApplication(BangChuangConsts.SettingsName.ThirdApi
                        .Validation_Code_Sms_ChangePassword_TemplateCode);
                case ValidationCodeType.SetWithdrawPassword:
                    return SettingManager.GetSettingValueForApplication(BangChuangConsts.SettingsName.ThirdApi
                        .Validation_Code_Sms_SetWithdrawPassword_TemplateCode);
                default: return null;
            }
        }

        /// <summary>
        /// 验证码校验
        /// </summary>
        /// <param name="phoneNumber"></param>
        /// <param name="code"></param>
        /// <param name="codeType"></param>
        /// <returns></returns>
        public async Task<bool> ValidationAsync(string phoneNumber, string code, ValidationCodeType codeType) {
            var query = _validationCodeRepository.GetAll()
                .Where(c => c.Target == phoneNumber)
                .Where(c => c.TargetType == ValidationCodeTargetType.PhoneNumber.ToString())
                .Where(c => c.ValidateType == codeType.ToString())
                .Where(c => c.IsObsolete == false)
                .Where(c => c.IsValidated == false)
                .OrderByDescending(c => c.CreationTime);
            var lastValidation = await Task.Run(() => query.FirstOrDefault());

            if(lastValidation == null || lastValidation.Code != code) {
                return false;
            }

            lastValidation.IsValidated = true;
            await _validationCodeRepository.UpdateAsync(lastValidation);
            return true;
        }

        /// <summary>
        /// 验证码校验
        /// </summary>
        /// <param name="phoneNumber"></param>
        /// <param name="code"></param>
        /// <param name="codeType"></param>
        /// <returns></returns>
        public bool Validation(string phoneNumber, string code, ValidationCodeType codeType) {
            return AsyncHelper.RunSync(() => ValidationAsync(phoneNumber, code, codeType));
        }
    }
}